package org.apache.poi.hssf.contrib.view;

import java.awt.*;

import javax.swing.border.AbstractBorder;

import org.apache.poi.hssf.usermodel.HSSFCellStyle;

/**
 * This is an attempt to implement Excel style borders for the SheetViewer.
 * Mostly just overrides stuff so the javadoc won't appear here but will 
 * appear in the generated stuff.
 * 
 * @author Andrew C. Oliver (acoliver at apache dot org)
 * @author Jason Height
 */
public class SVBorder extends AbstractBorder {
  private Color northColor = null;
  private Color eastColor = null;
  private Color southColor = null;
  private Color westColor = null;
  private int northBorderType = HSSFCellStyle.BORDER_NONE;
  private int eastBorderType =HSSFCellStyle.BORDER_NONE;
  private int southBorderType = HSSFCellStyle.BORDER_NONE;
  private int westBorderType = HSSFCellStyle.BORDER_NONE;
  private boolean northBorder=false;
  private boolean eastBorder=false;
  private boolean southBorder=false;
  private boolean westBorder=false;
  private boolean selected = false;

   public void setBorder(Color northColor, Color eastColor,
                         Color southColor, Color westColor,
                         int northBorderType, int eastBorderType,
                         int southBorderType, int westBorderType,
                         boolean selected) {
     this.eastColor = eastColor;
     this.southColor = southColor;
     this.westColor = westColor;
     this.northBorderType = northBorderType;
     this.eastBorderType = eastBorderType;
     this.southBorderType = southBorderType;
     this.westBorderType = westBorderType;
     this.northBorder=northBorderType != HSSFCellStyle.BORDER_NONE;
     this.eastBorder=eastBorderType != HSSFCellStyle.BORDER_NONE;
     this.southBorder=southBorderType != HSSFCellStyle.BORDER_NONE;
     this.westBorder=westBorderType != HSSFCellStyle.BORDER_NONE;
     this.selected = selected;
   }

   public void paintBorder(Component c, Graphics g, int x, int y, int width,
                           int height) {
      Color oldColor = g.getColor();


     paintSelectedBorder(g, x, y, width, height);
     paintNormalBorders(g, x, y, width, height);
     paintDottedBorders(g, x, y, width, height);
     paintDashedBorders(g, x, y, width, height);
     paintDoubleBorders(g, x, y, width, height);
     paintDashDotDotBorders(g, x, y, width, height);


     g.setColor(oldColor);
   }

   /**
    * Called by paintBorder to paint the border of a selected cell.
    * The paramaters are the Graphics object, location and dimensions of the 
    * cell.
    */
   private void paintSelectedBorder(Graphics g, int x, int y, int width,
                                  int height) {
     if (selected) {
       //Need to setup thickness of 2
       g.setColor(Color.black);
       //paint the border
       g.drawRect(x,y,width-1,height-1);

       //paint the filled rectangle at the bottom left hand position
       g.fillRect(x+width-5, y+height-5, 5, 5);
     }
   }


   /**
    * Called by paintBorder to paint the various versions of normal line
    * borders for a cell.  
    */
   private void paintNormalBorders(Graphics g, int x, int y, int width,
                                  int height) {

      if (northBorder &&
             ((northBorderType == HSSFCellStyle.BORDER_THIN) ||
              (northBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
              (northBorderType == HSSFCellStyle.BORDER_THICK)
             )
         ) {

        int thickness = getThickness(northBorderType);

      	g.setColor(northColor);

        for (int k=0; k < thickness; k++) {
           g.drawLine(x,y+k,width,y+k);
        }
      }

      if (eastBorder &&
             ((eastBorderType == HSSFCellStyle.BORDER_THIN) ||
              (eastBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
              (eastBorderType == HSSFCellStyle.BORDER_THICK)
             )
         ) {

        int thickness = getThickness(eastBorderType);

      	g.setColor(eastColor);

        for (int k=0; k < thickness; k++) {
           g.drawLine(width-k,y,width-k,height);
        }
      }

      if (southBorder &&
              ((southBorderType == HSSFCellStyle.BORDER_THIN) ||
               (southBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
               (southBorderType == HSSFCellStyle.BORDER_THICK)
              )
         ) {

        int thickness = getThickness(southBorderType);

      	g.setColor(southColor);
        for (int k=0; k < thickness; k++) {
           g.drawLine(x,height - k,width,height - k);
        }
      }

      if (westBorder &&
             ((westBorderType == HSSFCellStyle.BORDER_THIN) ||
              (westBorderType == HSSFCellStyle.BORDER_MEDIUM) ||
              (westBorderType == HSSFCellStyle.BORDER_THICK)
             )
         ) {

        int thickness = getThickness(westBorderType);

      	g.setColor(westColor);

        for (int k=0; k < thickness; k++) {
           g.drawLine(x+k,y,x+k,height);
        }
      }
   }

   /**
    * Called by paintBorder to paint the dotted line
    * borders for a cell.  
    */
   private void paintDottedBorders(Graphics g, int x, int y, int width,
                                  int height) {
      if (northBorder &&
             northBorderType == HSSFCellStyle.BORDER_DOTTED) {
        int thickness = getThickness(northBorderType);

      	g.setColor(northColor);

        for (int k=0; k < thickness; k++) {
           for (int xc = x; xc < width; xc=xc+2) {
             g.drawLine(xc,y+k,xc,y+k);
           }
        }
      }

      if (eastBorder &&
              eastBorderType == HSSFCellStyle.BORDER_DOTTED
         ) {

        int thickness = getThickness(eastBorderType);
        thickness++; //need for dotted borders to show up east

      	g.setColor(eastColor);

        for (int k=0; k < thickness; k++) {
           for (int yc=y;yc < height; yc=yc+2) {
                g.drawLine(width-k,yc,width-k,yc);
           }
        }
      }

      if (southBorder &&
              southBorderType == HSSFCellStyle.BORDER_DOTTED
         ) {

        int thickness = getThickness(southBorderType);
        thickness++;
      	g.setColor(southColor);
        for (int k=0; k < thickness; k++) {
           for (int xc = x; xc < width; xc=xc+2) {
             g.drawLine(xc,height-k,xc,height-k);
           }
        }
      }

      if (westBorder &&
            westBorderType == HSSFCellStyle.BORDER_DOTTED
         ) {

        int thickness = getThickness(westBorderType);
//        thickness++;

      	g.setColor(westColor);

        for (int k=0; k < thickness; k++) {
           for (int yc=y;yc < height; yc=yc+2) {
                g.drawLine(x+k,yc,x+k,yc);
           }
        }
      }
   }

   /**
    * Called by paintBorder to paint the various versions of dotted line
    * borders for a cell.  
    */
   private void paintDashedBorders(Graphics g, int x, int y, int width,
                                  int height) {
      if (northBorder &&
             ((northBorderType == HSSFCellStyle.BORDER_DASHED) ||
              (northBorderType == HSSFCellStyle.BORDER_HAIR))
         ) {
        int thickness = getThickness(northBorderType);

        int dashlength = 1;

        if (northBorderType == HSSFCellStyle.BORDER_DASHED)
           dashlength = 2;

      	g.setColor(northColor);

        for (int k=0; k < thickness; k++) {
           for (int xc = x; xc < width; xc=xc+5) {
             g.drawLine(xc,y+k,xc+dashlength,y+k);
           }
        }
      }

      if (eastBorder &&
              ((eastBorderType == HSSFCellStyle.BORDER_DASHED) ||
               (eastBorderType == HSSFCellStyle.BORDER_HAIR))
         ) {

        int thickness = getThickness(eastBorderType);
        thickness++; //need for dotted borders to show up east


        int dashlength = 1;

        if (eastBorderType == HSSFCellStyle.BORDER_DASHED)
           dashlength = 2;

      	g.setColor(eastColor);

        for (int k=0; k < thickness; k++) {
           for (int yc=y;yc < height; yc=yc+5) {
                g.drawLine(width-k,yc,width-k,yc+dashlength);
           }
        }
      }

      if (southBorder &&
              ((southBorderType == HSSFCellStyle.BORDER_DASHED) ||
               (southBorderType == HSSFCellStyle.BORDER_HAIR))
         ) {

        int thickness = getThickness(southBorderType);
        thickness++;

        int dashlength = 1;

        if (southBorderType == HSSFCellStyle.BORDER_DASHED)
           dashlength = 2;

      	g.setColor(southColor);
        for (int k=0; k < thickness; k++) {
           for (int xc = x; xc < width; xc=xc+5) {
             g.drawLine(xc,height-k,xc+dashlength,height-k);
           }
        }
      }

      if (westBorder &&
            ((westBorderType == HSSFCellStyle.BORDER_DASHED) ||
             (westBorderType == HSSFCellStyle.BORDER_HAIR))
         ) {

        int thickness = getThickness(westBorderType);
//        thickness++;

        int dashlength = 1;

        if (westBorderType == HSSFCellStyle.BORDER_DASHED)
           dashlength = 2;

      	g.setColor(westColor);

        for (int k=0; k < thickness; k++) {
           for (int yc=y;yc < height; yc=yc+5) {
                g.drawLine(x+k,yc,x+k,yc+dashlength);
           }
        }
      }
   }

   /**
    * Called by paintBorder to paint the double line
    * borders for a cell.  
    */
   private void paintDoubleBorders(Graphics g, int x, int y, int width,
                                  int height) {
      if (northBorder &&
             northBorderType == HSSFCellStyle.BORDER_DOUBLE) {

      	g.setColor(northColor);

        int leftx=x;
        int rightx=width;

                // if there are borders on the west or east then
                // the second line shouldn't cross them
        if (westBorder)
           leftx = x+3;

        if (eastBorder)
           rightx = width-3;

           g.drawLine(x,y,width,y);
           g.drawLine(leftx,y+2,rightx,y+2);
      }

      if (eastBorder &&
              eastBorderType == HSSFCellStyle.BORDER_DOUBLE
         ) {

        int thickness = getThickness(eastBorderType);
        thickness++; //need for dotted borders to show up east

      	g.setColor(eastColor);

        int topy=y;
        int bottomy=height;

        if (northBorder)
          topy=y+3;

        if (southBorder)
            bottomy=height-3;

        g.drawLine(width-1,y,width-1,height);
        g.drawLine(width-3,topy,width-3,bottomy);
      }

      if (southBorder &&
              southBorderType == HSSFCellStyle.BORDER_DOUBLE
         ) {

      	g.setColor(southColor);

        int leftx=y;
        int rightx=width;

        if (westBorder)
           leftx=x+3;

        if (eastBorder)
           rightx=width-3;


        g.drawLine(x,height - 1,width,height - 1);
        g.drawLine(leftx,height - 3,rightx,height - 3);
      }

      if (westBorder &&
            westBorderType == HSSFCellStyle.BORDER_DOUBLE
         ) {

        int thickness = getThickness(westBorderType);
//        thickness++;

      	g.setColor(westColor);

        int topy=y;
        int bottomy=height-3;

        if (northBorder)
           topy=y+2;

        if (southBorder)
           bottomy=height-3;

        g.drawLine(x,y,x,height);
        g.drawLine(x+2,topy,x+2,bottomy);
      }
   }

   /**
    * Called by paintBorder to paint the various versions of dash dot dot line
    * borders for a cell.  
    */
   private void paintDashDotDotBorders(Graphics g, int x, int y, int width,
                                  int height) {
      if (northBorder &&
             ((northBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
              (northBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
         ) {
        int thickness = getThickness(northBorderType);

      	g.setColor(northColor);
        for (int l=x; l < width;) {
          l=l+drawDashDotDot(g, l, y, thickness, true, true);
        }

      }

      if (eastBorder &&
              ((eastBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
               (eastBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
         ) {

        int thickness = getThickness(eastBorderType);

      	g.setColor(eastColor);

        for (int l=y;l < height;) {
          //System.err.println("drawing east");
          l=l+drawDashDotDot(g,width-1,l,thickness,false,false);
        }
      }

      if (southBorder &&
              ((southBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
               (southBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
         ) {

        int thickness = getThickness(southBorderType);

      	g.setColor(southColor);

        for (int l=x; l < width;) {
          //System.err.println("drawing south");
          l=l+drawDashDotDot(g, l, height-1, thickness, true, false);
        }
      }

      if (westBorder &&
            ((westBorderType == HSSFCellStyle.BORDER_DASH_DOT_DOT) ||
             (westBorderType == HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT))
         ) {

        int thickness = getThickness(westBorderType);

      	g.setColor(westColor);

        for (int l=y;l < height;) {
          //System.err.println("drawing west");
          l=l+drawDashDotDot(g,x,l,thickness,false,true);
        }

      }
   }

   /**
    *  Draws one dash dot dot horizontally or vertically with thickness drawn
    *  incrementally to either the right or left.
    *
    *  @param g graphics object for drawing with
    *  @param x the x origin of the line
    *  @param y the y origin of the line
    *  @param thickness the thickness of the line
    *  @param horizontal or vertical (true for horizontal)
    *  @param right/bottom or left/top thickness (true for right or top),
    *         if true then the x or y origin will be incremented to provide
    *         thickness, if false, they'll be decremented.  For vertical
    *         borders, x is incremented or decremented, for horizontal its y.
    *         Just set to true for north and west, and false for east and
    *         south.
    *  @returns length - returns the length of the line.
    */
   private int drawDashDotDot(Graphics g,int x, int y, int thickness,
                              boolean horizontal,
                              boolean rightBottom) {

      for (int t=0; t < thickness; t++) {
         if (!rightBottom) {
            t = 0 - t; //add negative thickness so we go the other way
                       //then we'll decrement instead of increment.
         }
         if (horizontal) {
            g.drawLine(x,y+t,x+5,y+t);
            g.drawLine(x+8,y+t,x+10,y+t);
            g.drawLine(x+13,y+t,x+15,y+t);
         } else {
            g.drawLine(x+t,y,x+t,y+5);
            g.drawLine(x+t,y+8,x+t,y+10);
            g.drawLine(x+t,y+13,x+t,y+15);
         }
      }
      return 18;
   }

   /**
    * @returns the line thickness for a border based on border type
    */
   private int getThickness(int thickness) {
       int retval=1;
       switch (thickness) {
           case HSSFCellStyle.BORDER_THIN:
             retval=2;
             break;
           case HSSFCellStyle.BORDER_MEDIUM:
             retval=3;
             break;
           case HSSFCellStyle.BORDER_THICK:
             retval=4;
             break;
           case HSSFCellStyle.BORDER_DASHED:
             retval=1;
             break;
           case HSSFCellStyle.BORDER_DASH_DOT_DOT:
             retval=1;
             break;
           case HSSFCellStyle.BORDER_MEDIUM_DASH_DOT_DOT:
             retval=3;
             break;
           case HSSFCellStyle.BORDER_HAIR:
             retval=1;
             break;
           default:
             retval=1;
       }
       return retval;
   }


}
